// Copyright 2011 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.


/**
 * @fileoverview Unifies the bite.layer subsystem within the context of
 * content scripts.  The constructor as a the initializer for the subsystem
 * causes the rest of the system to initialize.
 *
 * @author jasonstredwick@google.com (Jason Stredwick)
 */


goog.provide('bite.layer.client.Content');

goog.require('bite.common.mvc.helper');
goog.require('bite.layer.Messages');
goog.require('bite.layer.Status');
goog.require('bite.layer.soy.Common');



/**
 * Constructs an object that manages the layer UX.
 * @constructor
 */
bite.layer.client.Content = function() {
  /**
   * Whether or not consoles are initialized.
   * @type {boolean}
   * @private
   */
  this.ready_ = false;

  /**
   * A string representing the base url for relative references.
   * @type {string}
   * @private
   */
  this.baseUrl_ = '';
};
goog.addSingletonGetter(bite.layer.client.Content);


/**
 * Initializes the Layer UX for content scripts.
 * @param {string=} opt_baseUrl The base url to use for relative references.
 * @return {boolean} Whether or not the layer UI is ready.
 */
bite.layer.client.Content.prototype.init = function(opt_baseUrl) {
  if (this.ready) {
    return this.ready_;
  }

  this.baseUrl_ = opt_baseUrl || '';

  // Initialize controllers.
  // TODO (jasonstredwick): Add some form of undoInit to clean up spare
  // <style> tags.
  var helper = bite.common.mvc.helper;
  if (helper.initView(bite.layer.soy.Common.getView, this.baseUrl_) &&
      bite.layer.Status.getInstance().init(this.baseUrl_)) {
    this.setupConnections_();
  } else {
    return this.ready_;
  }

  // Let background know that we are ready and can be opened.
  var data = {};
  data[bite.layer.Messages.Request.OWNER] = bite.layer.Messages.Commands.OWNER;
  data[bite.layer.Messages.Request.ACTION] =
      bite.layer.Messages.Commands.CONTENT_READY;
  chrome.extension.sendRequest(data, goog.bind(this.initComplete_, this));

  // Set up layer UX message handler.
  chrome.extension.onRequest.addListener(goog.bind(this.onRequest_, this));

  this.ready_ = true;

  return this.ready_;
};


/**
 * Handles the response from the background after sending the message
 * CONTENT_READY.
 * @param {Object} data The data sent from the background.
 * @private
 */
bite.layer.client.Content.prototype.initComplete_ = function(data) {
  var owner = data[bite.layer.Messages.Request.OWNER];
  var action = data[bite.layer.Messages.Request.ACTION];
  var visible = data[bite.layer.Messages.Request.VISIBLE];
  if (owner != bite.layer.Messages.Commands.OWNER ||
      action != bite.layer.Messages.Commands.UPDATE ||
      !visible) {
    return;
  }

  if (!bite.layer.client.Content.getStatus_().isVisible()) {
    bite.layer.client.Content.getStatus_().show();
  }

  var layersDetails = data[bite.layer.Messages.Request.LAYERS_DETAILS];
  if (layersDetails) {
    bite.layer.Status.getInstance().reset(layersDetails);
  }
};


/**
 * Whether or not the view initialized.
 * @return {boolean} Returns whether or not the view is initialized.
 */
bite.layer.client.Content.prototype.isReady = function() {
  return this.ready_;
};


/**
 * Handles messages for the layer subsystem and redirects as appropriate.
 * @param {!Object} request The data sent.
 * @param {MessageSender} sender An object containing information about the
 *     script context that sent the request.
 * @param {function(!*): void} response Optional function to call when the
 *     request completes; only call when appropriate.
 * @private
 */
bite.layer.client.Content.prototype.onRequest_ =
    function(request, sender, response) {
  if (!this.ready_) {
    return;
  }

  var owner = request[bite.layer.Messages.Request.OWNER];
  var action = request[bite.layer.Messages.Request.ACTION];
  if (owner != bite.layer.Messages.Commands.OWNER || !action) {
    return;
  }

  switch (action) {
    case bite.layer.Messages.Commands.OPEN:
      if (!bite.layer.client.Content.getStatus_().isVisible()) {
        bite.layer.client.Content.getStatus_().show();
      }

      var layersDetails = request[bite.layer.Messages.Request.LAYERS_DETAILS];
      if (layersDetails) {
        bite.layer.Status.getInstance().reset(layersDetails);
      }

      break;

    case bite.layer.Messages.Commands.CLOSE:
      if (bite.layer.client.Content.getStatus_().isVisible()) {
        bite.layer.client.Content.getStatus_().hide();
      }
  }
};


/**
 * Connects the UI functionality to each other and to the server.
 * @private
 */
bite.layer.client.Content.prototype.setupConnections_ = function() {
  var status = bite.layer.Status.getInstance();

  // Connect Status
  goog.events.listen(status, bite.layer.StatusEventType.CLOSE,
      bite.layer.client.Content.handleStatus_Close_);
  goog.events.listen(status, bite.layer.StatusEventType.RESET,
      bite.layer.client.Content.handleStatus_Reset_);
  goog.events.listen(status, bite.layer.StatusEventType.RESET_MANAGER,
      bite.layer.client.Content.handleStatus_ResetManager_);
  goog.events.listen(status, bite.layer.StatusEventType.RESTORE_DEFAULTS,
      bite.layer.client.Content.handleStatus_RestoreDefaults_);
  goog.events.listen(status, bite.layer.StatusEventType.SET_ONDEMAND,
      bite.layer.client.Content.handleStatus_SetOnDemand_);
  goog.events.listen(status, bite.layer.StatusEventType.SET_STATE,
      bite.layer.client.Content.handleStatus_SetState_);
};


/**
 * Retrieves the Status Console and converts it to an object in order to
 * execute its behaviors.
 * @return {Object} The console as a plain object.
 * @private
 */
bite.layer.client.Content.getStatus_ = function() {
  return /** @type {Object} */ (bite.layer.Status.getInstance());
};


/**
 * Handles the event when the Close button is pressed in the Status console.
 * @private
 */
bite.layer.client.Content.handleStatus_Close_ = function() {
  var data = {};
  data[bite.layer.Messages.Request.OWNER] = bite.layer.Messages.Commands.OWNER;
  data[bite.layer.Messages.Request.ACTION] =
      bite.layer.Messages.Commands.CLOSE;
  chrome.extension.sendRequest(data);

  bite.layer.client.Content.getStatus_().hide();
};


/**
 * Handles the event when the Reset button is pressed in the Status console.
 * @param {common.events.Event} event The event fired from the console.
 * @private
 */
bite.layer.client.Content.handleStatus_Reset_ = function(event) {
  var id = event.getData()[bite.layer.Status.Data.ID];

  var data = {};
  data[bite.layer.Messages.Request.OWNER] = bite.layer.Messages.Commands.OWNER;
  data[bite.layer.Messages.Data.ID] = id;
  data[bite.layer.Messages.Request.ACTION] =
      bite.layer.Messages.Commands.RESET;
  chrome.extension.sendRequest(data,
      bite.layer.client.Content.handleStatus_ResetComplete_);
};


/**
 * Handles response from the background to update a single layer's information.
 * @param {{id: string, dataStatus: string, loadImmediate: string,
 *          state: string, status: string}=} opt_data Optional data to update
 *     a specific layers information.
 * @private
 */
bite.layer.client.Content.handleStatus_ResetComplete_ = function(opt_data) {
  if (opt_data) {
    bite.layer.Status.getInstance().update(opt_data);
  }
};


/**
 * Handles the event when the Reset Manager button is pressed in the Status
 * console.
 * @private
 */
bite.layer.client.Content.handleStatus_ResetManager_ = function() {
  var data = {};
  data[bite.layer.Messages.Request.OWNER] = bite.layer.Messages.Commands.OWNER;
  data[bite.layer.Messages.Request.ACTION] =
      bite.layer.Messages.Commands.RESET_MANAGER;
  chrome.extension.sendRequest(data,
      bite.layer.client.Content.handleStatus_ResetManagerComplete_);
};


/**
 * Handles the response from the background when restoring the default layer
 * list.  The response contains an update for status page.
 * @param {Array.<{id: string, dataStatus: string, loadImmediate: string,
 *                 state: string, status: string}>} data The data used to
 *     update the rows.
 * @private
 */
bite.layer.client.Content.handleStatus_ResetManagerComplete_ =
    function(data) {
  if (data) {
    bite.layer.Status.getInstance().reset(data);
  }
};


/**
 * Handles the event when the Restore Defaults button is pressed in the Status
 * console.
 * @private
 */
bite.layer.client.Content.handleStatus_RestoreDefaults_ = function() {
  var data = {};
  data[bite.layer.Messages.Request.OWNER] = bite.layer.Messages.Commands.OWNER;
  data[bite.layer.Messages.Request.ACTION] =
      bite.layer.Messages.Commands.RESTORE_DEFAULTS;
  chrome.extension.sendRequest(data,
      bite.layer.client.Content.handleStatus_RestoreDefaultsComplete_);
};


/**
 * Handles the response from the background when restoring the default layer
 * list.  The response contains an update for status page.
 * @param {Array.<{id: string, dataStatus: string, loadImmediate: string,
 *                 state: string, status: string}>} data The data used to
 *     update the rows.
 * @private
 */
bite.layer.client.Content.handleStatus_RestoreDefaultsComplete_ =
    function(data) {
  if (data) {
    bite.layer.Status.getInstance().reset(data);
  }
};


/**
 * Handles the event when the Set OnDemand button is pressed in the Status
 * console.
 * @param {common.events.Event} event The event fired from the console.
 * @private
 */
bite.layer.client.Content.handleStatus_SetOnDemand_ = function(event) {
  var id = event.getData()[bite.layer.Status.Data.ID];
  var value = event.getData()[bite.layer.Status.Data.VALUE];

  var data = {};
  data[bite.layer.Messages.Request.OWNER] = bite.layer.Messages.Commands.OWNER;
  data[bite.layer.Messages.Data.ID] = id;
  data[bite.layer.Messages.Data.VALUE] = value;
  data[bite.layer.Messages.Request.ACTION] =
      bite.layer.Messages.Commands.SET_ONDEMAND;
  chrome.extension.sendRequest(data,
      bite.layer.client.Content.handleStatus_SetOnDemandComplete_);
};


/**
 * Handles response from the background to update a single layer's information.
 * @param {{id: string, dataStatus: string, loadImmediate: string,
 *          state: string, status: string}=} opt_data Optional data to update
 *     a specific layers information.
 * @private
 */
bite.layer.client.Content.handleStatus_SetOnDemandComplete_ =
    function(opt_data) {
  if (opt_data) {
    bite.layer.Status.getInstance().update(opt_data);
  }
};


/**
 * Handles the event when the Set State button is pressed in the Status
 * console.
 * @param {common.events.Event} event The event fired from the console.
 * @private
 */
bite.layer.client.Content.handleStatus_SetState_ = function(event) {
  var id = event.getData()[bite.layer.Status.Data.ID];
  var value = event.getData()[bite.layer.Status.Data.VALUE];

  var data = {};
  data[bite.layer.Messages.Request.OWNER] = bite.layer.Messages.Commands.OWNER;
  data[bite.layer.Messages.Data.ID] = id;
  data[bite.layer.Messages.Data.VALUE] = value;
  data[bite.layer.Messages.Request.ACTION] =
      bite.layer.Messages.Commands.SET_STATE;
  chrome.extension.sendRequest(data,
      bite.layer.client.Content.handleStatus_SetStateComplete_);
};


/**
 * Handles response from the background to update a single layer's information.
 * @param {{id: string, dataStatus: string, loadImmediate: string,
 *          state: string, status: string}=} opt_data Optional data to update
 *     a specific layers information.
 * @private
 */
bite.layer.client.Content.handleStatus_SetStateComplete_ =
    function(opt_data) {
  if (opt_data) {
    bite.layer.Status.getInstance().update(opt_data);
  }
};

